package com.dianzhihan.http5;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@Controller
public class UserController {
    // 1. 支持 POST
    // 2. 资源路径是 /register.do
    @PostMapping("/register.do")
    // 3. 希望结果是重定向，所以，不需要使用 @ResponseBody 注解修饰方法
    // 4. 方法的返回值类型是 String，到时候会返回 "redirect:..."
    // 5. 需要读取用户提交的 <input type="text" name="username" placeholder="用户名"> 和 <input type="text" name="password" placeholder="密码">
    // 5. 所以，方法的形参有两个，分别使用 @RequestParam 修饰
    public String register(@RequestParam("username") String username, @RequestParam("password") String password) {
        System.out.println("用户注册：username = " + username + ", password = " + password);

        // 1. TODO: 本来应该要去完成的参数合法性校验
        // 2. 执行 SQL 语句
        String sql = "insert into users (username, password) values (?, ?)";
        try (Connection c = DBUtil.connection()) {
            try (PreparedStatement ps = c.prepareStatement(sql)) {
                ps.setString(1, username);
                ps.setString(2, password);

                ps.executeUpdate();
            }
        } catch (SQLException exc) {
            System.out.println("用户注册失败");
            exc.printStackTrace(System.out);
                return "redirect:/register.html";   // 失败后重定向到 注册页
        }

        System.out.println("用户注册成功");
        return "redirect:/login.html";    // 目前用户最终看到 404
    }

    // 准备 POST /login.do 动态资源
    @PostMapping("/login.do")
    // 要重定向，不需要 @ResponseBody 注解修饰方法
    // 返回值类型是 String
    // 要读取用户输入的用户名 + 密码信息，和注册一样
    // 要创建 session 对象，所以，形参中有 HttpServletRequest request
    public String login(
            @RequestParam("username") String usernameInput,
            @RequestParam("password") String passwordInput,
            HttpServletRequest request) {
        System.out.println("用户登录：username = " + usernameInput + ", password = " + passwordInput);
        // TODO: 参数合法性校验

        // 通过数据库查询该用户的信息
//        Integer uid = null;     // 使用 Integer 而不是 int，是 Integer 可以保存 null 这种特殊值
//        String username = null;
//        String password = null;
        User user = null;

        try (Connection c = DBUtil.connection()) {
            String sql = "select uid, username, password from users where username = ? and password = ?";
            try (PreparedStatement ps = c.prepareStatement(sql)) {
                ps.setString(1, usernameInput); // 把用户填写的用户名添加到 SQL 参数
                ps.setString(2, passwordInput); // 同理，密码

                // 带结果的，并且结果只有 1 条 或者 0 条
                try (ResultSet rs = ps.executeQuery()) {
                    if (rs.next()) {
                        // 查询到了 1 条记录，说明用户名 + 密码正确
                        int uid = rs.getInt("uid");
                        String username = rs.getString("username");    // 实际上，这两个字段可以不查询
                        String password = rs.getString("password");    // 实际上，这两个字段可以不查询

                        user = new User(uid, username, password);
                    } else {
                        // 一条记录都没有查询到，这里什么都不干
                        // 这里加上 else 的目的是为了写这个注释，以后我就不写了
                    }
                }
            }
        } catch (SQLException exc) {
            System.out.println("登录失败 by 数据库 SQL 执行失败，如果符合 HTTP 语义，应该是 500 错误");
            exc.printStackTrace(System.out);
            return "redirect:/login.html";
        }

        // 根据 uid 或者 username 或者 password 值来判断用户是否登录成功了，其中一个就行了
        if (user == null) {
            // 登录失败了
            System.out.println("登录失败 by 用户输入的参数有错误，如果符合 HTTP 语义，应该是 4XX 错误");
            return "redirect:/login.html";
        }

        // 登录成功
        // 获取 session 对象
        HttpSession session = request.getSession(); // 不带参数，默认是 create = true
        // 由于咱之前很大可能是没有 session 的
        // 所以内部所作的工作是
        // 1. 创建一个随机的 <session-id>
        // 2. 为这个 <session-id> 分配一个 HttpSession 对象（Map<String, Object> 结构）
        // 3. 响应中设置 Cookie（Set-Cookie）：JSESSIONID=<session-id>
        // 4. 所以，咱应该能在前端看到浏览器中有个 JSESSIONID 的 cookie
        // 把当前用户的信息（uid、username、password）保存到 session 中，key 我们随意指定
        // currentUser.uid、currentUser.username、currentUser.password
//        session.setAttribute("currentUser.uid", uid);
//        session.setAttribute("currentUser.username", username);
//        session.setAttribute("currentUser.password", password);
        session.setAttribute("currentUser", user);

        System.out.println("登录成功");
        return "redirect:/";    // 404
    }

    // GET /
    @GetMapping("/")
    // 在响应体中输入内容，所以 @ResponseBody 注解修饰方法 + 方法返回值类型是 String
    // 响应的 Content-Type 是 HTML，返回值直接当成 HTML 内容在对待
    // 由于需要获取 HttpSession 对象，所以，形参中有 HttpServletRequest request
    @ResponseBody
    public String getCurrentUser(HttpServletRequest request) {
//        Integer uid = null;
//        String username = null;
//        String password = null;
        User currentUser = null;

        HttpSession session = request.getSession(false);
        if (session != null) {
            System.out.println("有 session 对象");
            // 说明是有 Session 的
            // 获取的时候，必须保证 key 和 登录场景下 setAttribute 时的 key 一致
            // getAttribute(...) 的返回值类型是 Object
            // Object 是 Integer 的上级类型
            // Object -> Integer 是向下转型
            // 向下转型是不自然的，是有风险的，所以程序员（我们）明确告诉编译器是我要转的，职责我承担（有异常我自己弄）
            // 需要通过类型强制转换 (Integer)
//            uid = (Integer) session.getAttribute("currentUser.uid");
//            username = (String) session.getAttribute("currentUser.username");
//            password = (String) session.getAttribute("currentUser.password");
            currentUser = (User) session.getAttribute("currentUser");

            // 如果当时没有设置，getAttribute 的返回值是 null
        } else {
            System.out.println("没有 session 对象");
            // 反之没有 session，什么都不需要做
        }

        if (currentUser == null) {
            System.out.println("没有拿到 uid");
            // session 可能是 null 或者 session.getAttribute("currentUser.uid") 是 null
            // 不管是哪种可能，都代表本次请求-响应的用户，没有经历过登录的流程，也就是用户未登录
            return "用户未登录，请到 <a href='/login.html'>登录页</a> 进行登录";
        } else {
            System.out.println("拿到 uid 了");
//            return String.format("当前登录用户为: uid = %d, username = %s, password = %s", uid, username, password);
            return String.format("当前登录用户为: %s", currentUser);   // 这里虽然没写，但实际就是调用的 currnetUser.toString()
        }
    }

    @GetMapping("/quit.do")
    public String quit(HttpServletRequest request) {
        HttpSession session = request.getSession();
//        session.removeAttribute("currentUser.uid");
//        session.removeAttribute("currentUser.username");
//        session.removeAttribute("currentUser.password");
        session.removeAttribute("currentUser");
        System.out.println("退出登录 | 根本没有管是否登录过");

        return "redirect:/";
    }
}
